/* 
 *  <copyright file="ActorGameService.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using SobrietyEngine.Common;
using SobrietyEngine.Components;
using SobrietyEngine.Logging;
using SobrietyEngine.Scene;


namespace SobrietyEngine.Actor
{
    public class ActorGameService:DrawableGameComponent, SobrietyEngine.Actor.IActorGameService
    {

        public ActorLibrary ActorLibrary { get; set; }

        Dictionary<String, List<SobrietyEngine.Components.IComponent>> ComponentRegistry;
        Dictionary<String, List<Parameters>> ActorTemplateRegistry = null;

        public ActorGameService(Game game)
            : base(game)
        {
            ComponentRegistry = new Dictionary<string, List<SobrietyEngine.Components.IComponent>>();
            ComponentRegistry.Add("Graphic", new List<SobrietyEngine.Components.IComponent>());

            Game.Services.AddService(typeof(IActorGameService), this);            
        }

        public override void Initialize()
        {
            base.Initialize();
            ActorLibrary = new ActorLibrary(((SobrietyEngine.Games.SobrietyGame)Game).AssetGameService);
        }        

        public void LoadActorTemplates(String Path)
        {
            ActorLibrary.Load(Path);
            AddTemplatesToFactory();
        }

        //this is hacky. clean this up
        public void AddTemplatesToFactory()
        {
            foreach (ActorTemplate at in ActorLibrary.Templates.Values)
            {
                ActorFactory.Register(new Actor.Builders.ActorDefinition(at));
            }
        }

        public void RegisterActorTemplate(string AssetName)
        {
            if (ActorTemplateRegistry == null)
                ActorTemplateRegistry = new Dictionary<string,List<Parameters>>();
            
            ActorTemplateRegistry.Add(AssetName, Game.Content.Load<List<Parameters>>(AssetName));
        }

        //creates an actor with specified settings
        public IActor CreateActor(string actorType, SobrietyEngine.Scene.IScene scene, ActorTemplate instanceParameters)
        {
            return CreateActorInternal(actorType, scene, instanceParameters);
        }

        public IActor CreateActor(String ID, String actorType, IScene scene)
        {
            IActor actor = CreateActor(actorType, scene);
            IComponent comp = new Components.Common.IdCmp(ID);
            actor.AttachComponent(comp);
            return actor;
        }

        public IActor CreateActor(String actorType, IScene scene)
        {
            return CreateActorInternal(actorType, scene, null);
        }

        public IActor CreateActor(String actorType)
        {
            return CreateActorInternal(actorType, null, null);
        }

        private IActor CreateActorInternal(String actorType, IScene scene, ActorTemplate instance)
        {
            if (!ActorLibrary.Templates.ContainsKey(actorType))
                Logger.GetSingleton().WriteError("ActorGameService::CreateActorFromTemplate():Actor type not found in library");

            ActorTemplate template = ActorLibrary.Templates[actorType];
            
            IActor temp = scene.CreateEmptyActor();

            if (scene != null)
                temp = (scene as IScene).CreateEmptyActor();
            else
                temp = new BaseActor(Game, scene);

            //if we have an args defined
            if (instance != null)
            {
                //we need to merge the actorDefinition with the passed in isntance,
                //overwriting values in the actorDefinition that already exist
                ActorTemplate merged = new ActorTemplate();

                //copy compnent params from actorDefinition
                foreach (String compName in template.Components.Keys)
                    merged.Components[compName] = new Parameters(template.Components[compName]);

                //go through all component params in args
                foreach(String compName in instance.Components.Keys)
                {
                    //merge params together if they arleady exist
                    if (merged.Components.ContainsKey(compName))
                    {
                        merged.Components[compName] = merged.Components[compName].Merge(instance.Components[compName], true);
                    }
                    //just move over the args stuff
                    else
                        merged.Components[compName] = instance.Components[compName];
                }

                //set the tempalte to be this new merged actorDefinition, then construct away
                template = merged;                
            }








            foreach (String comp in template.Components.Keys)
            {
                SobrietyEngine.Components.IComponent cmp;
                cmp = ComponentFactory.Build(comp);
                temp.AttachComponent(cmp);

                try
                {
                    //check to make sure we ID the args correctly
                    //iuf we have an args and theid comp, set its value
                    if (instance != null)
                    {
                        if (instance.Name != String.Empty)
                        {
                            if (cmp.GetType() == typeof(IId))
                                ((IId)cmp).Id = instance.Name;
                        }
                    }

                    //no need to call set if no parameters
                    if (template.Components[comp].Params.Count != 0)
                        cmp.Set(template.Components[comp]);
                }
                catch (Exception ex)
                {
                    Logger.Error("ComponentFactory:: error in Set(). likely not implemented on " + cmp.GetType().ToString() + "\n" + ex.Message);
                }                               
            }

            //check to make sure we ID the args correctly
            //check for names. if we have n args, and no id comp, create and attach it
            if (instance != null)
            {
                if (instance.Name != String.Empty)
                {
                    IId id = temp.Get<IId>();
                    if (id == null)
                        temp.AttachComponent(new Components.Common.IdCmp(instance.Name));
                }
            }

            return temp;
        }
    }
}